package ca.compsci.opent.compiler.optimizer;

import ca.compsci.opent.compiler.node.*;
import ca.compsci.opent.compiler.semantics.*;

/**
 * The ArithmeticEvaluator will perform arithmetic operations
 * on the given literals, based on the bin_opr involved.
 * 
 * The evaluator tries to guarantee safe evaluations, i.e. no
 * precision is lost as far as possible. In certain cases
 * this might not be avoidable, in which case, the evaluator
 * will throw an appropriate exception.
 * 
 * @author rizzix
 */
public class ArithmeticEvaluator extends Evaluator<Number> {
	private Literal left, right;
	private Number result;
	private Type promoted_type;
	
	public ArithmeticEvaluator(Literal l, Literal r, Type promoted_type) {
		this.left = l;
		this.right = r;
		this.promoted_type = promoted_type;
	}
	
	public Number result() {
		return this.result;
	}
	
	public Type resultantType() {
		return this.promoted_type;
	}
	
	@Override
	public void caseAAddOpr(AAddOpr node) {
		if (isIntegralType(promoted_type)) {
			long left_num = ((Number)left.getValue()).longValue();
			long right_num = ((Number)right.getValue()).longValue();
			result = left_num + right_num;
		} else {
			double left_num = ((Number)left.getValue()).doubleValue();
			double right_num = ((Number)right.getValue()).doubleValue();
			result = left_num + right_num;
		}
	}

	@Override
	public void caseASubOpr(ASubOpr node) {
		if (isIntegralType(promoted_type)) {
			long left_num = ((Number)left.getValue()).longValue();
			long right_num = ((Number)right.getValue()).longValue();
			result = left_num - right_num;
		} else {
			double left_num = ((Number)left.getValue()).doubleValue();
			double right_num = ((Number)right.getValue()).doubleValue();
			result = left_num - right_num;
		}
	}

	@Override
	public void caseAMulOpr(AMulOpr node) {
		if (isIntegralType(promoted_type)) {
			long left_num = ((Number)left.getValue()).longValue();
			long right_num = ((Number)right.getValue()).longValue();
			result = left_num * right_num;
		} else {
			double left_num = ((Number)left.getValue()).doubleValue();
			double right_num = ((Number)right.getValue()).doubleValue();
			result = left_num * right_num;
		}
	}

	@Override
	public void caseADivOpr(ADivOpr node) {
		if (isIntegralType(promoted_type)) {
			long left_num = ((Number)left.getValue()).longValue();
			long right_num = ((Number)right.getValue()).longValue();
			result = left_num / right_num;
		} else {
			double left_num = ((Number)left.getValue()).doubleValue();
			double right_num = ((Number)right.getValue()).doubleValue();
			result = left_num / right_num;
		}
	}

	@Override
	public void caseAModOpr(AModOpr node) {
		if (isIntegralType(promoted_type)) {
			long left_num = ((Number)left.getValue()).longValue();
			long right_num = ((Number)right.getValue()).longValue();
			result = left_num % right_num;
		} else {
			double left_num = ((Number)left.getValue()).doubleValue();
			double right_num = ((Number)right.getValue()).doubleValue();
			result = left_num % right_num;
		}
	}

	@Override
	public void caseAPowerOpr(APowerOpr node) {
		double left_num = ((Number)left.getValue()).doubleValue();
		double right_num = ((Number)right.getValue()).doubleValue();
		if (isIntegralType(promoted_type))
			result = Double.valueOf(Math.pow(left_num, right_num)).longValue();
		else
			result = Math.pow(left_num, right_num);
	}

	@Override
	public void caseAShlOpr(AShlOpr node) {
		long left_num = ((Number)left.getValue()).longValue();
		long right_num = ((Number)right.getValue()).longValue();
		result = left_num << right_num;
	}

	@Override
	public void caseAShrOpr(AShrOpr node) {
		long left_num = ((Number)left.getValue()).longValue();
		long right_num = ((Number)right.getValue()).longValue();
		result = left_num >> right_num;
	}
	
	// we consider only Reals and Integers
	private boolean isIntegralType(Type t) {
		switch (t) {
		case Real32:
		case Real64:
			return false;
		default:
			return true;
		}
	}
}